//
//  MFPixelBufferHelper.m
//  MFPanoramaPlayerDemo
//
//  Created by Lyman Li on 2020/1/25.
//  Copyright © 2020 Lyman Li. All rights reserved.
//

#import "MFShaderHelper.h"

#import "MFPixelBufferHelper.h"
#import <GLKit/GLKit.h>
@import OpenGLES;

#define STRINGIZE(x) #x
#define STRINGIZE2(x) STRINGIZE(x)
#define SHADER_STRING(text) @ STRINGIZE2(text)

enum {
    HWD_UNIFORM_Y,
    HWD_UNIFORM_UV,
    HWD_UNIFORM_COLOR_CONVERSION_MATRIX,
    HWD_NUM_UNIFORMS
};
GLint hwd_uniforms[HWD_NUM_UNIFORMS];

// Attribute index.
enum {
    ATTRIB_VERTEX,
    ATTRIB_TEXCOORD_RGB,
    ATTRIB_TEXCOORD_ALPHA,
    NUM_ATTRIBUTES
};
typedef NS_ENUM(NSInteger, QGHWDTextureBlendMode){
    
    QGHWDTextureBlendMode_AlphaLeft                 = 0,          // 左侧alpha
    QGHWDTextureBlendMode_AlphaRight                = 1,          // 右侧alpha
    QGHWDTextureBlendMode_AlphaTop                  = 2,          // 上侧alpha
    QGHWDTextureBlendMode_AlphaBottom               = 3,          // 下测alpha
};

// BT.709-HDTV.
//static const GLfloat kColorConversion709[] = {
//    1.164,  1.164, 1.164,
//    0.0, -0.213, 2.112,
//    1.793, -0.533,   0.0,
//};
//
//// BT.601 full range-http://www.equasys.de/colorconversion.html
//const GLfloat kColorConversion601FullRange[] = {
//    1.0,    1.0,    1.0,
//    0.0,    -0.343, 1.765,
//    1.4,    -0.711, 0.0,
//};


// texture coords for blend

const GLfloat textureCoordLeft[] =  { // 左侧
    0.5, 0.0,
    0.0, 0.0,
    0.5, 1.0,
    0.0, 1.0
};

const GLfloat textureCoordRight[] =  { // 右侧
    1.0, 0.0,
    0.5, 0.0,
    1.0, 1.0,
    0.5, 1.0
};

const GLfloat textureCoordTop[] =  { // 上侧
    1.0, 0.0,
    0.0, 0.0,
    1.0, 0.5,
    0.0, 0.5
};

const GLfloat textureCoordBottom[] =  { // 下侧
    1.0, 0.5,
    0.0, 0.5,
    1.0, 1.0,
    0.0, 1.0
};

#undef cos
#undef sin
NSString *const kVertexShaderSource = SHADER_STRING
(
 attribute vec4 position;
 attribute vec2 RGBTexCoord;
 attribute vec2 alphaTexCoord;

 varying vec2 RGBTexCoordVarying;
 varying vec2 alphaTexCoordVarying;

 void main()
{
    float preferredRotation = 3.14;
    mat4 rotationMatrix = mat4(cos(preferredRotation), -sin(preferredRotation), 0.0, 0.0,sin(preferredRotation),cos(preferredRotation), 0.0, 0.0,0.0,0.0,1.0,0.0,0.0,0.0, 0.0,1.0);
    gl_Position = rotationMatrix * position;
    RGBTexCoordVarying = RGBTexCoord;
    alphaTexCoordVarying = alphaTexCoord;
}
 );

NSString *const kFragmentShaderSource = SHADER_STRING
(
 varying highp vec2 RGBTexCoordVarying;
 varying highp vec2 alphaTexCoordVarying;
 precision mediump float;

 uniform sampler2D SamplerY;
 uniform sampler2D SamplerUV;
 uniform mat3 colorConversionMatrix;

 void main()
{
    mediump vec3 yuv_rgb;
    lowp vec3 rgb_rgb;

    mediump vec3 yuv_alpha;
    lowp vec3 rgb_alpha;

    // Subtract constants to map the video range start at 0
    yuv_rgb.x = (texture2D(SamplerY, RGBTexCoordVarying).r);// - (16.0/255.0));
    yuv_rgb.yz = (texture2D(SamplerUV, RGBTexCoordVarying).ra - vec2(0.5, 0.5));

    rgb_rgb = colorConversionMatrix * yuv_rgb;


    yuv_alpha.x = (texture2D(SamplerY, alphaTexCoordVarying).r);// - (16.0/255.0));
    yuv_alpha.yz = (texture2D(SamplerUV, alphaTexCoordVarying).ra - vec2(0.4, 0.4));

    rgb_alpha = colorConversionMatrix * yuv_alpha;


    gl_FragColor = vec4(rgb_rgb,rgb_alpha.r);
//        gl_FragColor = vec4(1, 0, 0, 1);
}
 );
@interface MFPixelBufferHelper ()
{
    float z;
    GLuint pkFrameBuffer;
    GLuint _backingWidth;
    GLuint _backingHeight;
    GLuint _frameBufferHandle;
    const GLfloat *_preferredConversion;
}
@property (nonatomic, strong) EAGLContext *context;
//第一轨道
@property (nonatomic, assign) GLuint yuvConversionProgram;
//第二轨道
@property(nonatomic,assign)GLuint secondConversionProgram;
///第一轨道
@property (nonatomic, assign) GLuint normalProgram;
///第二轨道
@property(nonatomic,assign)GLuint sNormalProgram;

///透明(第二轨道)
@property(nonatomic,assign)GLuint opaqueProgram;
//透明视频的RGB
@property(nonatomic,assign)GLuint opaqueNormalProgram;

@property (nonatomic, assign) CVOpenGLESTextureCacheRef textureCache;

@property (nonatomic, assign) GLuint VBO;

@property (nonatomic, assign) CVOpenGLESTextureRef luminanceTexture;
@property (nonatomic, assign) CVOpenGLESTextureRef chrominanceTexture;
@property (nonatomic, assign) CVOpenGLESTextureRef renderTexture;
@property (nonatomic, assign) QGHWDTextureBlendMode blendMode;

@end

@implementation MFPixelBufferHelper

- (void)dealloc {
    if (_luminanceTexture) {
        CFRelease(_luminanceTexture);
    }
    if (_chrominanceTexture) {
        CFRelease(_chrominanceTexture);
    }
    if (_renderTexture) {
        CFRelease(_renderTexture);
    }
    if (_textureCache) {
        CFRelease(_textureCache);
    }
    if (_yuvConversionProgram) {
        glDeleteProgram(_yuvConversionProgram);
    }
    if(_normalProgram){
        glDeleteProgram(_normalProgram);
    }
    if(_secondConversionProgram){
        glDeleteProgram(_secondConversionProgram);
    }
    if(_sNormalProgram){
        glDeleteProgram(_sNormalProgram);
    }
    if(pkFrameBuffer){
        glDeleteFramebuffers(1, &pkFrameBuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }
    if (_VBO) {
        glDeleteBuffers(1, &_VBO);
    }
}

- (instancetype)initWithContext:(EAGLContext *)context {
    self = [super init];
    if (self) {
        _context = context;
        [self setupYUVConversionProgram];
      
        [self setupNormalProgram];
        [self setupVBO];
        [self setupSecondNormalProgram];
        [self setupSecondProgram];
        [self setupOpaqueNormalProgram];
        [self loadShaders];
        glGenFramebuffers(1, &_frameBufferHandle);
        glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle);
        self.blendMode = QGHWDTextureBlendMode_AlphaRight;
        _preferredConversion = kColorConversion601FullRange;
        glGenFramebuffers(1, &pkFrameBuffer);
        
        z=10;
    }
    return self;
}

#pragma mark - Accessors

- (CVOpenGLESTextureCacheRef)textureCache {
    if (!_textureCache) {
        EAGLContext *context = self.context;
        CVReturn status = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, nil, context, nil, &_textureCache);
        if (status != kCVReturnSuccess) {
            NSLog(@"Can't create textureCache");
        }
    }
    return _textureCache;
}

- (void)setLuminanceTexture:(CVOpenGLESTextureRef)luminanceTexture {
    if (_luminanceTexture &&
        luminanceTexture &&
        CFEqual(luminanceTexture, _luminanceTexture)) {
        return;
    }
    if (luminanceTexture) {
        CFRetain(luminanceTexture);
    }
    if (_luminanceTexture) {
        CFRelease(_luminanceTexture);
    }
    _luminanceTexture = luminanceTexture;
}

- (void)setChrominanceTexture:(CVOpenGLESTextureRef)chrominanceTexture {
    if (_chrominanceTexture &&
        chrominanceTexture &&
        CFEqual(chrominanceTexture, _chrominanceTexture)) {
        return;
    }
    if (chrominanceTexture) {
        CFRetain(chrominanceTexture);
    }
    if (_chrominanceTexture) {
        CFRelease(_chrominanceTexture);
    }
    _chrominanceTexture = chrominanceTexture;
}

- (void)setRenderTexture:(CVOpenGLESTextureRef)renderTexture {
    if (_renderTexture &&
        renderTexture &&
        CFEqual(renderTexture, _renderTexture)) {
        return;
    }
    if (renderTexture) {
        CFRetain(renderTexture);
    }
    if (_renderTexture) {
        CFRelease(_renderTexture);
    }
    _renderTexture = renderTexture;
}

#pragma mark - Public

- (CVPixelBufferRef)createPixelBufferWithSize:(CGSize)size {
    CVPixelBufferRef pixelBuffer;
    NSDictionary *pixelBufferAttributes = @{(id)kCVPixelBufferIOSurfacePropertiesKey: @{}};
    CVReturn status = CVPixelBufferCreate(nil,
                                          size.width,
                                          size.height,
                                          kCVPixelFormatType_32BGRA,
                                          (__bridge CFDictionaryRef _Nullable)(pixelBufferAttributes),
                                          &pixelBuffer);
    if (status != kCVReturnSuccess) {
        NSLog(@"Can't create pixelbuffer");
    }
    return pixelBuffer;
}
- (void)cleanupTextures {
    
    if (_luminanceTexture) {
        CFRelease(_luminanceTexture);
        _luminanceTexture = NULL;
    }
    if (_chrominanceTexture) {
        CFRelease(_chrominanceTexture);
        _chrominanceTexture = NULL;
    }
    CVOpenGLESTextureCacheFlush(_textureCache, 0);
}
#pragma mark --Vap透明视频的YUV 格式的 PixelBuffer 转化为纹理
-(GLuint)convertOpaqueYUVPixelBufferToTexTure:(CVPixelBufferRef)pixelBuffer
{
    if (!pixelBuffer) {
        NSLog(@"获取帧数据失败");
        return 0;
    }
    if ([EAGLContext currentContext] != _context) {
        [EAGLContext setCurrentContext:_context];
    }
    CGSize videoSize = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer));
  
    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);
    GLuint textureID;

    CVReturn err;
    _backingWidth = videoSize.width == 1440? videoSize.width:videoSize.width-16;
    _backingHeight = videoSize.height ==1280?videoSize.height :videoSize.height-16 ;
        if (!_textureCache) {
//            PKLog(@"No video texture cache");
//            return;
        }
    
    [self cleanupTextures];

    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _backingWidth, _backingHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

    glViewport(0, 0, _backingWidth, _backingHeight);
    CFTypeRef colorAttachments = CVBufferGetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, NULL);
    if(CFStringCompare(colorAttachments, kCVImageBufferYCbCrMatrix_ITU_R_601_4, 0 ) == kCFCompareEqualTo){
        _preferredConversion = kColorConversion601FullRange;
    }else{
        _preferredConversion = kColorConversion709;
    }
    CVOpenGLESTextureRef luminanceTextureRef = nil;
    CVOpenGLESTextureRef chrominanceTextureRef = nil;
        //Y
        err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                           self.textureCache,
                                                           pixelBuffer,
                                                           NULL,
                                                           GL_TEXTURE_2D,
                                                           GL_LUMINANCE,
                                                           _backingWidth,
                                                           _backingHeight,
                                                           GL_LUMINANCE,
                                                           GL_UNSIGNED_BYTE,
                                                           0,
                                                           &luminanceTextureRef);
        if (err) {
//            PKLog(@"Error at CVOpenGLESTextureCacheCreateTextureFromImage");
        }


        //UV
//        glActiveTexture(GL_TEXTURE1);
        err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                           self.textureCache,
                                                           pixelBuffer,
                                                           NULL,
                                                           GL_TEXTURE_2D,
                                                           GL_LUMINANCE_ALPHA,
                                                           _backingWidth / 2.0,
                                                           _backingHeight/2.0,
                                                           GL_LUMINANCE_ALPHA,
                                                           GL_UNSIGNED_BYTE,
                                                           1,
                                                           &chrominanceTextureRef);
        if (err) {
//            PKLog(@"Error at CVOpenGLESTextureCacheCreateTextureFromImage  UV=== %d",err);
//            VAP_Error(kQGVAPModuleCommon, @"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d",  err);
        }
    glActiveTexture(GL_TEXTURE6);
    glBindTexture(CVOpenGLESTextureGetTarget(luminanceTextureRef), CVOpenGLESTextureGetName(luminanceTextureRef));
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glActiveTexture(GL_TEXTURE7);
    glBindTexture(CVOpenGLESTextureGetTarget(chrominanceTextureRef), CVOpenGLESTextureGetName(chrominanceTextureRef));
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);




    glViewport(0, 0, _backingWidth, _backingHeight);

    glUseProgram(self.opaqueProgram);
    glUniform1i(hwd_uniforms[HWD_UNIFORM_Y], 6);
    glUniform1i(hwd_uniforms[HWD_UNIFORM_UV], 7);
    glUniformMatrix3fv(hwd_uniforms[HWD_UNIFORM_COLOR_CONVERSION_MATRIX], 1, GL_FALSE, _preferredConversion);
    
//    // 根据视频的方向和高宽比设置四个顶点。
    CGRect vertexRect = AVMakeRectWithAspectRatioInsideRect(CGSizeMake(_backingWidth, _backingHeight), CGRectMake(0, 0, _backingWidth/2, _backingHeight/2));
    CGRect layerRect = CGRectMake(0, 0, _backingWidth/2, _backingHeight/2);
    // 计算归一化四坐标来绘制坐标系。
    CGSize normalizedSamplingSize = CGSizeMake(0.0, 0.0);
    CGSize cropScaleAmount = CGSizeMake(vertexRect.size.width/layerRect.size.width, vertexRect.size.height/layerRect.size.height);
    
    if (cropScaleAmount.width > cropScaleAmount.height) {
        normalizedSamplingSize.width = 1.0;
        normalizedSamplingSize.height = cropScaleAmount.height/cropScaleAmount.width;
    } else {
        normalizedSamplingSize.width = 1.0;
        normalizedSamplingSize.height = cropScaleAmount.width/cropScaleAmount.height;
    }
    
    GLfloat quadVertexData [] = {
        -1 * normalizedSamplingSize.width, -1 * normalizedSamplingSize.height,
        normalizedSamplingSize.width, -1 * normalizedSamplingSize.height,
        -1 * normalizedSamplingSize.width, normalizedSamplingSize.height,
        normalizedSamplingSize.width, normalizedSamplingSize.height,
    };
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, quadVertexData);
    glEnableVertexAttribArray(ATTRIB_VERTEX);
    glVertexAttribPointer(ATTRIB_TEXCOORD_RGB, 2, GL_FLOAT, GL_FALSE, 0, [self quadTextureRGBData]);
    glEnableVertexAttribArray(ATTRIB_TEXCOORD_RGB);
    glVertexAttribPointer(ATTRIB_TEXCOORD_ALPHA, 2, GL_FLOAT, GL_FALSE, 0, [self quedTextureAlphaData]);
    glEnableVertexAttribArray(ATTRIB_TEXCOORD_ALPHA);
    // 更新顶点数据
    @try {
     
        
    } @catch (NSException *exception) {
        
    } @finally {
        
    }
    

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//
//    glDeleteFramebuffers(1, &_frameBufferHandle);
//    glBindFramebuffer(GL_FRAMEBUFFER, 0);
//    glBindBuffer(GL_ARRAY_BUFFER, 0);
    self.luminanceTexture = luminanceTextureRef;
    self.chrominanceTexture = chrominanceTextureRef;
    CFRelease(luminanceTextureRef);
    CFRelease(chrominanceTextureRef);

    glFlush();
//    _backingWidth = 0;
    return textureID;
}
- (const void *)quedTextureAlphaData {

    switch (self.blendMode) {
        case QGHWDTextureBlendMode_AlphaLeft:
            return textureCoordLeft;
        case QGHWDTextureBlendMode_AlphaRight:
            return textureCoordRight;
        case QGHWDTextureBlendMode_AlphaTop:
            return textureCoordTop;
        case QGHWDTextureBlendMode_AlphaBottom:
            return textureCoordBottom;
        default:
            return textureCoordLeft;
    }
}

- (const void *)quadTextureRGBData {

    switch (self.blendMode) {
        case QGHWDTextureBlendMode_AlphaLeft:
            return textureCoordRight;
        case QGHWDTextureBlendMode_AlphaRight:
            return textureCoordLeft;
        case QGHWDTextureBlendMode_AlphaTop:
            return textureCoordBottom;
        case QGHWDTextureBlendMode_AlphaBottom:
            return textureCoordTop;
        default:
            return textureCoordRight;
    }
}

-(void)gl_TexParameteri
{
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}




#pragma mark --YUV 格式的 PixelBuffer 转化为纹理
- (GLuint)convertYUVPixelBufferToTexture:(CVPixelBufferRef)pixelBuffer {
    if (!pixelBuffer) {
        return 0;
    }
    
    CGSize textureSize = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer),
                                    CVPixelBufferGetHeight(pixelBuffer));

    [EAGLContext setCurrentContext:self.context];
    
    //这里这么写单纯为了用来判断是VAP视频还是普通视频
    _backingWidth = 720;
//    GLuint frameBuffer;
    GLuint textureID;
    
    // FBO
//    glGenFramebuffers(1, &frameBuffer);
//    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, pkFrameBuffer);
    
    // texture
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.width, textureSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
    
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
    
    glViewport(0, 0, 720, 1280);
    
    // program
    glUseProgram(self.yuvConversionProgram);
    
    // texture
    CVOpenGLESTextureRef luminanceTextureRef = nil;
    CVOpenGLESTextureRef chrominanceTextureRef = nil;

    CVReturn status = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                                   self.textureCache,
                                                                   pixelBuffer,
                                                                   nil,
                                                                   GL_TEXTURE_2D,
                                                                   GL_LUMINANCE,
                                                                   textureSize.width,
                                                                   textureSize.height,
                                                                   GL_LUMINANCE,
                                                                   GL_UNSIGNED_BYTE,
                                                                   0,
                                                                   &luminanceTextureRef);
    if (status != kCVReturnSuccess) {
        NSLog(@"Can't create luminanceTexture");
    }
    
    status = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                          self.textureCache,
                                                          pixelBuffer,
                                                          nil,
                                                          GL_TEXTURE_2D,
                                                          GL_LUMINANCE_ALPHA,
                                                          textureSize.width / 2,
                                                          textureSize.height / 2,
                                                          GL_LUMINANCE_ALPHA,
                                                          GL_UNSIGNED_BYTE,
                                                          1,
                                                          &chrominanceTextureRef);
    
    if (status != kCVReturnSuccess) {
        NSLog(@"Can't create chrominanceTexture");
    }
    
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(luminanceTextureRef));
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glUniform1i(glGetUniformLocation(self.yuvConversionProgram, "luminanceTexture"), 0);
    
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(chrominanceTextureRef));
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glUniform1i(glGetUniformLocation(self.yuvConversionProgram, "chrominanceTexture"), 1);
    
    GLfloat kXDXPreViewColorConversion601FullRange[] = {
        1.0,    1.0,    1.0,
        0.0,    -0.343, 1.765,
        1.4,    -0.711, 0.0,
    };
    
    GLuint yuvConversionMatrixUniform = glGetUniformLocation(self.yuvConversionProgram, "colorConversionMatrix");
    glUniformMatrix3fv(yuvConversionMatrixUniform, 1, GL_FALSE, kXDXPreViewColorConversion601FullRange);
    
    // VBO
    glBindBuffer(GL_ARRAY_BUFFER, self.VBO);
    
    GLuint positionSlot = glGetAttribLocation(self.yuvConversionProgram, "position");
    glEnableVertexAttribArray(positionSlot);
    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    
    GLuint textureSlot = glGetAttribLocation(self.yuvConversionProgram, "inputTextureCoordinate");
    glEnableVertexAttribArray(textureSlot);
    glVertexAttribPointer(textureSlot, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3* sizeof(float)));
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
//    glDeleteFramebuffers(1, &frameBuffer);
    
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    glFlush();
    
    self.luminanceTexture = luminanceTextureRef;
    self.chrominanceTexture = chrominanceTextureRef;
    
    CFRelease(luminanceTextureRef);
    CFRelease(chrominanceTextureRef);
    
    return textureID;
}

- (GLuint)secondconvertYUVPixelBufferToTexture:(CVPixelBufferRef)pixelBuffer {
    if (!pixelBuffer) {
        return 0;
    }

    CGSize textureSize = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer),
                                    CVPixelBufferGetHeight(pixelBuffer));

    [EAGLContext setCurrentContext:self.context];
    
//    GLuint frameBuffer;
    GLuint textureID;
    
    // FBO
//    glGenFramebuffers(1, &frameBuffer);
//    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, pkFrameBuffer);
    // texture
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.width, textureSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
    
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
    
    glViewport(0,0, 720, 1280);
    
    // program
    glUseProgram(self.secondConversionProgram);
    
    // texture
    CVOpenGLESTextureRef luminanceTextureRef = nil;
    CVOpenGLESTextureRef chrominanceTextureRef = nil;

    CVReturn status = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                                   self.textureCache,
                                                                   pixelBuffer,
                                                                   nil,
                                                                   GL_TEXTURE_2D,
                                                                   GL_LUMINANCE,
                                                                   textureSize.width,
                                                                   textureSize.height,
                                                                   GL_LUMINANCE,
                                                                   GL_UNSIGNED_BYTE,
                                                                   0,
                                                                   &luminanceTextureRef);
    if (status != kCVReturnSuccess) {
        NSLog(@"Can't create luminanceTexture");
    }
    
    status = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                          self.textureCache,
                                                          pixelBuffer,
                                                          nil,
                                                          GL_TEXTURE_2D,
                                                          GL_LUMINANCE_ALPHA,
                                                          textureSize.width / 2,
                                                          textureSize.height / 2,
                                                          GL_LUMINANCE_ALPHA,
                                                          GL_UNSIGNED_BYTE,
                                                          1,
                                                          &chrominanceTextureRef);
    
    if (status != kCVReturnSuccess) {
        NSLog(@"Can't create chrominanceTexture");
    }
    
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(luminanceTextureRef));
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glUniform1i(glGetUniformLocation(self.secondConversionProgram, "luminanceTexture"), 2);
    
    glActiveTexture(GL_TEXTURE3);
    glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(chrominanceTextureRef));
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glUniform1i(glGetUniformLocation(self.secondConversionProgram, "chrominanceTexture"), 3);
    
    GLfloat kXDXPreViewColorConversion601FullRange[] = {
        1.0,    1.0,    1.0,
        0.0,    -0.343, 1.765,
        1.4,    -0.711, 0.0,
    };
    
    GLuint yuvConversionMatrixUniform = glGetUniformLocation(self.secondConversionProgram, "colorConversionMatrix");
    glUniformMatrix3fv(yuvConversionMatrixUniform, 1, GL_FALSE, kXDXPreViewColorConversion601FullRange);
   
    // VBO
    glBindBuffer(GL_ARRAY_BUFFER, self.VBO);
    
    GLuint positionSlot = glGetAttribLocation(self.secondConversionProgram, "position");
    glEnableVertexAttribArray(positionSlot);
    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    
    GLuint textureSlot = glGetAttribLocation(self.secondConversionProgram, "inputTextureCoordinate");
    glEnableVertexAttribArray(textureSlot);
    glVertexAttribPointer(textureSlot, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3* sizeof(float)));
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
//    glDeleteFramebuffers(1, &frameBuffer);
    
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    glFlush();
    
//    self.luminanceTexture = luminanceTextureRef;
//    self.chrominanceTexture = chrominanceTextureRef;
    
    CFRelease(luminanceTextureRef);
    CFRelease(chrominanceTextureRef);
    
    return textureID;
}
- (GLuint)convertRGBPixelBufferToTexture:(CVPixelBufferRef)pixelBuffer {
    if (!pixelBuffer) {
        return 0;
    }
    
    CGSize textureSize = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer),
                                    CVPixelBufferGetHeight(pixelBuffer));
    CVOpenGLESTextureRef texture = nil;
    
    CVReturn status = CVOpenGLESTextureCacheCreateTextureFromImage(nil,
                                                                   self.textureCache,
                                                                   pixelBuffer,
                                                                   nil,
                                                                   GL_TEXTURE_2D,
                                                                   GL_RGBA,
                                                                   textureSize.width,
                                                                   textureSize.height,
                                                                   GL_BGRA,
                                                                   GL_UNSIGNED_BYTE,
                                                                   0,
                                                                   &texture);
    
    if (status != kCVReturnSuccess) {
        NSLog(@"Can't create texture");
    }
    
    self.renderTexture = texture;
    CFRelease(texture);
    return CVOpenGLESTextureGetName(texture);
}

- (CVPixelBufferRef)convertTextureToPixelBuffer:(GLuint)texture
                                    textureSize:(CGSize)textureSize {
    [EAGLContext setCurrentContext:self.context];
    
    CVPixelBufferRef pixelBuffer = [self createPixelBufferWithSize:textureSize];
    GLuint targetTextureID = [self convertRGBPixelBufferToTexture:pixelBuffer];
    
    GLuint frameBuffer;
    
    // FBO
//    glGenFramebuffers(1, &frameBuffer);
//    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, pkFrameBuffer);
    
    // texture
    glBindTexture(GL_TEXTURE_2D, targetTextureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.width, textureSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTextureID, 0);
    
    glViewport(0, 0, 720, 1280);
    
    // program
    glUseProgram(self.normalProgram);
    
    // texture
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glUniform1i(glGetUniformLocation(self.normalProgram, "renderTexture"), 0);
    
    // VBO
    glBindBuffer(GL_ARRAY_BUFFER, self.VBO);
    
    GLuint positionSlot = glGetAttribLocation(self.normalProgram, "position");
    glEnableVertexAttribArray(positionSlot);
    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    
    GLuint textureSlot = glGetAttribLocation(self.normalProgram, "inputTextureCoordinate");
    glEnableVertexAttribArray(textureSlot);
    glVertexAttribPointer(textureSlot, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3* sizeof(float)));
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
//    glDeleteFramebuffers(1, &frameBuffer);
    
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    glFlush();
    
    return pixelBuffer;
}

#pragma mark -- 双视频纹理的转化
- (CVPixelBufferRef)doubleconvertTextureToPixelBuffer:(GLuint)texture withSecondPixelBuffer:(GLuint)stexture
                                    textureSize:(CGSize)textureSize {
    [EAGLContext setCurrentContext:self.context];
    glClearColor(0, 0, 0, 0) ;
    CVPixelBufferRef pixelBuffer = [self createPixelBufferWithSize:textureSize];
    GLuint targetTextureID = [self convertRGBPixelBufferToTexture:pixelBuffer];
    
    GLuint frameBuffer;
    
    // FBO
//    glGenFramebuffers(1, &frameBuffer);
//    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, pkFrameBuffer);
    // texture
    glBindTexture(GL_TEXTURE_2D, targetTextureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.width, textureSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTextureID, 0);
    
    glEnable(GL_DEPTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE,GL_ONE);
    
    glViewport(0, 0, 720, 1280);
    
    if(texture){
        glUseProgram(self.normalProgram);

        // texture
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glUniform1i(glGetUniformLocation(self.normalProgram, "renderTexture"), 0);

        float itime = 0.5;
    //    glUniform1f(glGetUniformLocation(self.normalProgram, "iTime"), itime++);

        glBindBuffer(GL_ARRAY_BUFFER, self.VBO);
        GLuint positionSlot = glGetAttribLocation(self.normalProgram, "position");
        glEnableVertexAttribArray(positionSlot);
        glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);

        GLuint textureSlot = glGetAttribLocation(self.normalProgram, "inputTextureCoordinate");
        glEnableVertexAttribArray(textureSlot);
        glVertexAttribPointer(textureSlot, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3* sizeof(float)));
        

        GLuint mMVPMatrixHandle = glGetUniformLocation(self.normalProgram, "vMatrix");

    //
    //
        z = z+ 1;
      GLKMatrix4   projectionMartrix = GLKMatrix4Identity;
        GLKMatrix4  viewMartrix = GLKMatrix4Identity;
        GLKMatrix4  modelMatrix = GLKMatrix4Identity;
        GLKMatrix4 mMVPMatrix = GLKMatrix4Identity;
        modelMatrix= GLKMatrix4Rotate(modelMatrix, GLKMathDegreesToRadians(0), 0, 0, 1.0);
    //    modelMatrix = GLKMatrix4Translate(modelMatrix, z, 0, 1.0);
    mMVPMatrix = GLKMatrix4Multiply(viewMartrix, modelMatrix);
    mMVPMatrix = GLKMatrix4Multiply(projectionMartrix, mMVPMatrix);
        glUniformMatrix4fv(mMVPMatrixHandle, 1, GL_FALSE, (GLfloat *)&mMVPMatrix);
        
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    }
    glFlush();
  
   
    glClearColor(0, 0, 0, 0);
//    glEnable(GL_DEPTH);

//    glBlendColor(0, 0, 0, 0);
    glEnable(GL_BLEND);

    // program
#pragma mark -- VAP透明视频的绘制
    if(stexture && _backingWidth == 1440){
        glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
        glViewport(0, 0, 720, 960);
        glUseProgram(self.opaqueNormalProgram);
        glBindBuffer(GL_ARRAY_BUFFER, self.VBO);
//
//
//
//
//        CGFloat widthRatio = 1;
//        CGFloat heightRatio = 1;
//////            PKAlbumSelectModal *selectModal = self.compileModal.modalArray[time.picNum-1];
//        CGRect realRect = AVMakeRectWithAspectRatioInsideRect(CGSizeMake(720, 960), CGRectMake(0, 0, 720, 1280));
//         widthRatio = realRect.size.width/(1440/2);
//        heightRatio = realRect.size.height/1280;
//        GLfloat vertextData[] = {
//            1*widthRatio, -1*heightRatio,0.0,    1.0f,0.0f, //右下
//            1*widthRatio,1*heightRatio,0.0,     1.0,1.0,//右上
//            -1*widthRatio,1*heightRatio,0.0,     0.0,1.0,//左上
//
//            1*widthRatio,-1*heightRatio,0.0,    1.0,0.0,//右下
//            -1*widthRatio,1*heightRatio,0.0,    0.0,1.0,//左上
//            -1*widthRatio,-1*heightRatio,0.0,     0.0,0.0,//左下
//        };
//        GLuint vbo;
//        glGenBuffers(1, &vbo);
//        glBindBuffer(GL_ARRAY_BUFFER, vbo);
//        glBufferData(GL_ARRAY_BUFFER, sizeof(vertextData), vertextData, GL_STATIC_DRAW);
//
//
//        glBindBuffer(GL_ARRAY_BUFFER, _VBO);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, stexture);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glUniform1i(glGetUniformLocation(self.opaqueNormalProgram, "renderTexture"), 1);
        GLuint positionSlot1 = glGetAttribLocation(self.opaqueNormalProgram, "position");
        glEnableVertexAttribArray(positionSlot1);
        glVertexAttribPointer(positionSlot1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);

        GLuint textureSlot1 = glGetAttribLocation(self.opaqueNormalProgram, "inputTextureCoordinate");
        glEnableVertexAttribArray(textureSlot1);
        glVertexAttribPointer(textureSlot1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3* sizeof(float)));
        GLuint mMVPMatrixHandle1 = glGetUniformLocation(self.opaqueNormalProgram, "vMatrix");
        // VBO
        GLKMatrix4 mMVPMatrix1 = GLKMatrix4Identity;
    //    modelMatrix= GLKMatrix4Rotate(modelMatrix, GLKMathDegreesToRadians(z), 0, 0, 0.2);
    //    modelMatrix = GLKMatrix4Translate(modelMatrix, z, 0, 1.0);
    //mMVPMatrix = GLKMatrix4Multiply(viewMartrix, modelMatrix);
    //mMVPMatrix = GLKMatrix4Multiply(projectionMartrix, mMVPMatrix);
        glUniformMatrix4fv(mMVPMatrixHandle1, 1, GL_FALSE, (GLfloat *)&mMVPMatrix1);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        

    }
#pragma mark --普通透明视频的绘制
//    glEnable(GL_BLEND);
//    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
//    glEnable(GL_DEPTH);
    if(stexture && _backingWidth == 720){
        glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
        glViewport(0, 0, 720, 1280);
        glUseProgram(self.sNormalProgram);
        glBindBuffer(GL_ARRAY_BUFFER, self.VBO);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, stexture);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glUniform1i(glGetUniformLocation(self.sNormalProgram, "renderTexture"), 1);
        GLuint positionSlot1 = glGetAttribLocation(self.sNormalProgram, "position");
        glEnableVertexAttribArray(positionSlot1);
        glVertexAttribPointer(positionSlot1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);

        GLuint textureSlot1 = glGetAttribLocation(self.sNormalProgram, "inputTextureCoordinate");
        glEnableVertexAttribArray(textureSlot1);
        glVertexAttribPointer(textureSlot1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3* sizeof(float)));
        GLuint mMVPMatrixHandle1 = glGetUniformLocation(self.sNormalProgram, "vMatrix");
        // VBO
        GLKMatrix4 mMVPMatrix1 = GLKMatrix4Identity;
    //    modelMatrix= GLKMatrix4Rotate(modelMatrix, GLKMathDegreesToRadians(z), 0, 0, 0.2);
    //    modelMatrix = GLKMatrix4Translate(modelMatrix, z, 0, 1.0);
    //mMVPMatrix = GLKMatrix4Multiply(viewMartrix, modelMatrix);
    //mMVPMatrix = GLKMatrix4Multiply(projectionMartrix, mMVPMatrix);
        glUniformMatrix4fv(mMVPMatrixHandle1, 1, GL_FALSE, (GLfloat *)&mMVPMatrix1);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    }
    
 


    
    glDeleteFramebuffers(1, &frameBuffer);
    
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    glFlush();
    glDisable(GL_BLEND);
    
    return pixelBuffer;
}


#pragma mark - Private

- (void)setupYUVConversionProgram {
    self.yuvConversionProgram = [MFShaderHelper programWithShaderName:@"YUVConversion"];
}

-(void)setupSecondProgram
{
    self.secondConversionProgram = [MFShaderHelper programWithShaderName:@"YUVConversion"];
}

- (void)setupNormalProgram {
    self.normalProgram = [MFShaderHelper programWithShaderName:@"Normal"];
}

-(void)setupSecondNormalProgram
{
    self.sNormalProgram = [MFShaderHelper programWithShaderName:@"mainNormal"];
}

-(void)setupOpaqueNormalProgram
{
    self.opaqueNormalProgram = [MFShaderHelper programWithShaderName:@"OpaqueNormal"];
}






-(void)setupOpaqueProgram
{
    self.opaqueProgram = [MFShaderHelper programWithShaderName:@"Opaque"];
    glBindAttribLocation(self.opaqueProgram, ATTRIB_VERTEX, "position");
    glBindAttribLocation(self.opaqueProgram, ATTRIB_TEXCOORD_RGB, "RGBTexCoord");
    glBindAttribLocation(self.opaqueProgram, ATTRIB_TEXCOORD_ALPHA, "alphaTexCoord");
    
    hwd_uniforms[HWD_UNIFORM_Y] = glGetUniformLocation(self.opaqueProgram, "SamplerY");
    hwd_uniforms[HWD_UNIFORM_UV] = glGetUniformLocation(self.opaqueProgram, "SamplerUV");
    hwd_uniforms[HWD_UNIFORM_COLOR_CONVERSION_MATRIX] = glGetUniformLocation(self.opaqueProgram, "colorConversionMatrix");
}

- (void)setupVBO {
    float vertices[] = {
        -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
        -1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
        1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
        1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
    };
    
    glGenBuffers(1, &_VBO);
    glBindBuffer(GL_ARRAY_BUFFER, _VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
}


- (BOOL)loadShaders {
    
    GLuint vShader, fShader;
    self.opaqueProgram = glCreateProgram();
    // Create and compile the vertex shader.
    if (![self compileShader:&vShader type:GL_VERTEX_SHADER source:kVertexShaderSource]) {
//        VAP_Error(kQGVAPModuleCommon, @"Failed to compile vertex shader");
        return NO;
    }
    // Create and compile fragment shader.
    if (![self compileShader:&fShader type:GL_FRAGMENT_SHADER source:kFragmentShaderSource]) {
//        VAP_Error(kQGVAPModuleCommon, @"Failed to compile fragment shader");
        return NO;
    }
    // Attach vertex shader to program.
    glAttachShader(self.opaqueProgram, vShader);
    // Attach fragment shader to program.
    glAttachShader(self.opaqueProgram, fShader);
    // Bind attribute locations. This needs to be done prior to linking.
    glBindAttribLocation(self.opaqueProgram, ATTRIB_VERTEX, "position");
    glBindAttribLocation(self.opaqueProgram, ATTRIB_TEXCOORD_RGB, "RGBTexCoord");
    glBindAttribLocation(self.opaqueProgram, ATTRIB_TEXCOORD_ALPHA, "alphaTexCoord");
    // Link the program.
    if (![self linkProgram:self.opaqueProgram]) {
//        VAP_Event(kQGVAPModuleCommon, @"Failed to link program: %d", self.program);
        if (vShader) {
            glDeleteShader(vShader);
            vShader = 0;
        }
        if (fShader) {
            glDeleteShader(fShader);
            fShader = 0;
        }
        if (self.opaqueProgram) {
            glDeleteProgram(self.opaqueProgram);
            self.opaqueProgram = 0;
        }
        return NO;
    }
    
    // Get uniforms' location.
    hwd_uniforms[HWD_UNIFORM_Y] = glGetUniformLocation(self.opaqueProgram, "SamplerY");
    hwd_uniforms[HWD_UNIFORM_UV] = glGetUniformLocation(self.opaqueProgram, "SamplerUV");
    hwd_uniforms[HWD_UNIFORM_COLOR_CONVERSION_MATRIX] = glGetUniformLocation(self.opaqueProgram, "colorConversionMatrix");
    
    // Release vertex and fragment shaders.
    if (vShader) {
        glDetachShader(self.opaqueProgram, vShader);
        glDeleteShader(vShader);
    }
    if (fShader) {
        glDetachShader(self.opaqueProgram, fShader);
        glDeleteShader(fShader);
    }
    return YES;
}

- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type source:(const NSString *)sourceString {
    
    GLint status;
    const GLchar *source;
    source = (GLchar *)[sourceString UTF8String];
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
#if defined(DEBUG)
    GLint lengthOfLog;
    glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &lengthOfLog);
    if (lengthOfLog > 0) {
        GLchar *log = (GLchar *)malloc(lengthOfLog);
        glGetShaderInfoLog(*shader, lengthOfLog, &lengthOfLog, log);
//        VAP_Info(kQGVAPModuleCommon, @"MODULE_DECODE Shader compile log:\n%s", log)
        free(log);
    }
#endif
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
    if (status == 0) {
        glDeleteShader(*shader);
        return NO;
    }
    return YES;
}

- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL {
    
//    VAP_Info(kQGVAPModuleCommon, @"compileShader");
    NSError *error;
    NSString *sourceString = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];
    if (sourceString == nil) {
        NSLog(@"编译着色器失败");
//        VAP_Event(kQGVAPModuleCommon, @"Failed to load vertex shader: %@", [error localizedDescription]);
        return NO;
    }
    
    const GLchar *source;
    source = (GLchar *)[sourceString UTF8String];
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
    
#if defined(DEBUG)
    GLint lengthOfLog;
    glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &lengthOfLog);
    if (lengthOfLog > 0) {
        GLchar *log = (GLchar *)malloc(lengthOfLog);
        glGetShaderInfoLog(*shader, lengthOfLog, &lengthOfLog, log);
//        VAP_Info(kQGVAPModuleCommon, @"Shader compile log:\n%s", log);
        free(log);
    }
#endif
    
    GLint status;
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
    if (status == 0) {
        glDeleteShader(*shader);
        return NO;
    }
    
    return YES;
}

- (BOOL)linkProgram:(GLuint)prog {
    
    GLint status;
    glLinkProgram(prog);
    
#if defined(DEBUG)
    GLint lengthOfLog;
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &lengthOfLog);
    if (lengthOfLog > 0) {
        GLchar *log = (GLchar *)malloc(lengthOfLog);
        glGetProgramInfoLog(prog, lengthOfLog, &lengthOfLog, log);
//        VAP_Info(kQGVAPModuleCommon, @"Program link log:\n%s", log);
        free(log);
    }
#endif
    
    glGetProgramiv(prog, GL_LINK_STATUS, &status);
    if (status == 0) {
        return NO;
    }
    
    return YES;
}

- (BOOL)isValidateProgram:(GLuint)prog {
    
    GLint logLength, status;
    glValidateProgram(prog);
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
//        VAP_Info(kQGVAPModuleCommon, @"Program validate log:\n%s", log);
        free(log);
    }
    
    glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
    if (status == 0) {
        NSLog(@"编译器无效");
//        VAP_Event(kQGVAPModuleCommon, @"program is not valid:%@",@(status));
        return NO;
    }
    NSLog(@"program 有效");
//    VAP_Info(kQGVAPModuleCommon, @"programe is valid");
    return YES;
}

@end

